#include <iostream>
using namespace std;
#include <thread>
#include <mutex>
#include <vector>
// 1:定义一个类，只能在堆上开辟空间
class HeapOnly
{
public:
    static HeapOnly *GetObj()
    {
        return new HeapOnly;
    }

private:
    HeapOnly() {}
    HeapOnly(const HeapOnly &s) = delete;
};

// 2:定义一个类，只能在栈上开辟空间
class StackOnly
{
public:
    static StackOnly GetObj()
    {
        return StackOnly();
    }

private:
    StackOnly()
    {
    }
    StackOnly(const StackOnly &S) = delete;
};

// 3:单例模式：懒汉模式
// 但是这里会存在线程安全问题
// 如果第一次有多个线程同时进行if判断，那么有可能
// 一个线程进去申请空间了，但是另一个线程已经进行过判断了
// 那么就可能会出现数据不一致问题
// 解决方法：加锁
class SingleMode
{
public:
    static SingleMode *GetObj()
    {
        // RAII锁+二次检查
        //只需要检查第一次进来的线程，后面进来的就不用检查
        if (_pinst == nullptr)
        {
            unique_lock<mutex> lock(_m);
            if (_pinst == nullptr)
            {
                _pinst = new SingleMode;
            }
        }

        return _pinst;
    }

    //手动释放
    static void DelSingleMode(){
        unique_lock<mutex> lock(_m);
        delete _pinst;
        _pinst = nullptr;
    }

private:
    SingleMode() {}
    SingleMode(const SingleMode &e) = delete;
    static SingleMode *_pinst; // 使用这个指针来操作
    static mutex _m;
};
SingleMode *SingleMode::_pinst = nullptr;
mutex SingleMode::_m;

//程序结束时候，正常释放单例模式的对象
class GC{
public:
    ~GC(){
        SingleMode::DelSingleMode();
    }
};
static GC gc;


int m3()
{
    vector<thread> vthreads;
    int n = 5;
    for (int i = 0; i < n; i++)
    {
        vthreads.push_back(thread([]()
                                  {
            cout<<this_thread::get_id()<<endl;
            cout<<SingleMode::GetObj()<<endl; }));
    }

    for (auto &e : vthreads)
    {
        e.join();
    }
    // cout<<SingleMode::GetObj()<<endl;
    // cout<<SingleMode::GetObj()<<endl;
    // cout<<SingleMode::GetObj()<<endl;

    // HeapOnly* ret = HeapOnly::GetObj();
    // StackOnly ret = StackOnly::GetObj();

    return 0;
}




// 4:单例模式：饿汉模式
//在main函数前就创建对象，并且只能创建一个对象
class HungerMode{
public:
    static HungerMode* Get(){
        if(&_pin == nullptr){
            return new HungerMode;
        }
        return &_pin;
    }

private:    
    static HungerMode _pin;
    HungerMode(){}
    HungerMode(const HungerMode&) =delete;
};
HungerMode HungerMode::_pin;